Analísis de Datos Exploratorio¶

IAGO GORDO OTERO Y JOSE MARÍA VEGA RAMIRO¶

Bank Account Fraud Dataset Suite¶

Este conjunto de datos contiene información y datos reales sobre la detección de fraudes en el momento de abrir una cuenta bancaria en línea. Con esta información podemos predecir y elaborar un modelo que nos guíe a la hora de conceder servicios financieros a clientes.

La variable objetivo es fraud_bool donde el 0 indica no fraude y el 1 fraude.

Descripción del problema¶

El objetivo es realizar todos los pasos hasta construir un modelo de Machine Learning que sea capaz de predecir la probabilidad de, si un cliente, en nuestro caso de una entidad financiera, es apropiado para concederle servicios financieros según la probabilidad de que pueda o no pueda hacer fraude.

Este enfoque proporcionará a las entidades financieras una herramienta poderosa para identificar con mayor precisión a los clientes con alto riesgo de cometer fraude, fortaleciendo así sus estrategias de prevención y protección financiera.

Pasos a realizar¶

1- Análisis de Datos: Iniciamos examinando los datos disponibles. Este paso implica entender su estructura, calidad y las relaciones entre las diferentes variables.

2- Transformaciones Necesarias: Basándonos en nuestro análisis, realizaremos las transformaciones apropiadas en los datos. Esto puede incluir la limpieza de datos (como eliminar valores atípicos o manejar valores faltantes), la normalización, y la ingeniería de características para hacer los datos más adecuados para el modelado.

3- Construcción de Modelos: Desarrollaremos varios modelos predictivos. Esto podría incluir una variedad de enfoques, como regresión, árboles de decisión, entre otros, dependiendo de la naturaleza del problema y los datos.

4- Evaluación y Selección del Modelo: Compararemos el rendimiento de los distintos modelos utilizando métricas adecuadas para la tarea, como la precisión, recall,F1 etc. El objetivo es identificar el modelo que ofrezca las predicciones más precisas.

5- Implementación del Modelo Seleccionado: Una vez identificado el modelo más eficaz, lo utilizaremos para realizar predicciones en nuevos datos.

Importamos las librerías que necesitamos para la realización del análisis descriptivo exploratorio.

In [1]:
import pandas as pd
import numpy as np
import seaborn as sns
from matplotlib import pyplot as plt
import plotly.express as px
import scipy.stats as ss
import matplotlib.ticker as ticker
import warnings
from sklearn.impute import KNNImputer
import warnings
from scipy.stats import chi2_contingency

pd.set_option('display.max_columns', 500)
pd.set_option('display.max_rows', 5000)

Definimos las funciones que usaremos durante el EDA.

In [2]:
def plot_feature(df, col_name, isContinuous, target):
    """
    Visualize a variable with and without faceting on the target status (fraud_bool).
    - df: DataFrame.
    - col_name: Name of the variable in the DataFrame.
    - isContinuous: True if the variable is continuous, False otherwise.
    - target: Name of the target column to facet on (fraud_bool).
    """
    f, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(12, 3), dpi=90)
    count_null = df[col_name].isnull().sum()

    color = '#5975A4' 

    if isContinuous:
        sns.histplot(df.loc[df[col_name].notnull(), col_name], kde=False, ax=ax1)
    else:
        sns.countplot(x=col_name, data=df, ax=ax1, color=color)

    ax1.set_title(f"{col_name} (Null count: {count_null})")
    ax1.set_xlabel(col_name)
    ax1.set_ylabel('Count')

    if isContinuous:
        sns.boxplot(x=col_name, y=target, hue=target, data=df, ax=ax2)
        ax2.set_title(f"{col_name} by {target}")
    else:
        proportions = (df.groupby(col_name)[target].value_counts(normalize=True)
                        .rename('proportion')
                        .reset_index())
        sns.barplot(x=col_name, y='proportion', hue=target, data=proportions, ax=ax2)

    ax2.set_title(f"{col_name} by {target} proportion")
    ax2.set_ylabel('Proportion')

    ax2.set_xlabel(col_name)
    
    plt.setp(ax1.xaxis.get_majorticklabels(), rotation=90)
    plt.setp(ax2.xaxis.get_majorticklabels(), rotation=90)

    plt.tight_layout()
    plt.show()


def get_corr_matrix(dataset = None, metodo='pearson', size_figure=[10,8]):

    if dataset is None:
        print(u'\nHace falta pasar argumentos a la función')
        return 1
    sns.set(style="white")
    corr = dataset.corr(method=metodo) 
    for i in range(corr.shape[0]):
        corr.iloc[i, i] = 0
    f, ax = plt.subplots(figsize=size_figure)
    sns.heatmap(corr, center=0,
                square=True, linewidths=.5,  cmap ='viridis' ) #cbar_kws={"shrink": .5}
    plt.show()
    
    return 0

def get_deviation_of_mean_perc(df, list_var_continuous, target, multiplier):
 
    pd_final = pd.DataFrame()
    
    for i in list_var_continuous:
        
        series_mean = df[i].mean()
        series_std = df[i].std()
        std_amp = multiplier * series_std
        left = series_mean - std_amp
        right = series_mean + std_amp
        size_s = df[i].size
        
        perc_goods = df[i][(df[i] >= left) & (df[i] <= right)].size/size_s
        perc_excess = df[i][(df[i] < left) | (df[i] > right)].size/size_s
        
        if perc_excess>0:    
            pd_concat_percent = pd.DataFrame(df[target][(df[i] < left) | (df[i] > right)]\
                                            .value_counts(normalize=True).reset_index()).T
            pd_concat_percent.columns = [pd_concat_percent.iloc[0,0], 
                                         pd_concat_percent.iloc[0,1]]
            pd_concat_percent = pd_concat_percent.drop('fraud_bool',axis=0)
            pd_concat_percent['variable'] = i
            pd_concat_percent['sum_outlier_values'] = df[i][(df[i] < left) | (df[i] > right)].size
            pd_concat_percent['porcentaje_sum_null_values'] = perc_excess
            pd_final = pd.concat([pd_final, pd_concat_percent], axis=0).reset_index(drop=True)
            
    if pd_final.empty:
        print('No existen variables con valores nulos')
        
    return pd_final
  


def get_percent_null_values_target(pd_loan, list_var_continuous, target):

    pd_final = pd.DataFrame()
    for i in list_var_continuous:
        if pd_loan[i].isnull().sum()>0:
            pd_concat_percent = pd.DataFrame(pd_loan[target][pd_loan[i].isnull()]\
                                            .value_counts(normalize=True).reset_index()).T
            pd_concat_percent.columns = [pd_concat_percent.iloc[0,0], 
                                         pd_concat_percent.iloc[0,1]]
            pd_concat_percent = pd_concat_percent.drop('index',axis=0)
            pd_concat_percent['variable'] = i
            pd_concat_percent['sum_null_values'] = pd_loan[i].isnull().sum()
            pd_concat_percent['porcentaje_sum_null_values'] = pd_loan[i].isnull().sum()/pd_loan.shape[0]
            pd_final = pd.concat([pd_final, pd_concat_percent], axis=0).reset_index(drop=True)
            
    if pd_final.empty:
        print('No existen variables con valores nulos')
        
    return pd_final



def cramers_v(confusion_matrix):

    chi2 = ss.chi2_contingency(confusion_matrix)[0]
    n = confusion_matrix.sum()
    phi2 = chi2 / n
    r, k = confusion_matrix.shape
    phi2corr = max(0, phi2 - ((k-1)*(r-1))/(n-1))
    rcorr = r - ((r-1)**2)/(n-1)
    kcorr = k - ((k-1)**2)/(n-1)
    return np.sqrt(phi2corr / min((kcorr-1), (rcorr-1)))

def duplicate_columns(frame):
    groups = frame.columns.to_series().groupby(frame.dtypes).groups
    dups = []

    for t, v in groups.items():

        cs = frame[v].columns
        vs = frame[v]
        lcs = len(cs)

        for i in range(lcs):
            ia = vs.iloc[:,i].values
            for j in range(i+1, lcs):
                ja = vs.iloc[:,j].values
                if np.array_equal(ia, ja):
                    dups.append(cs[i])
                    break
    return dups

def calcular_missing_por_fila(fila):
    missing_count = 0
    for col in fila.index:
        if col == 'intended_balcon_amount' and fila[col] < 0:
            missing_count += 1
        elif fila[col] == -1:
            missing_count += 1
    return missing_count

def cramers_V(var1,var2) :
    crosstab = np.array(pd.crosstab(var1,var2, rownames=None, colnames=None))
    stat = chi2_contingency(crosstab)[0]
    obs = np.sum(crosstab)
    mini = min(crosstab.shape)-1
    return (stat/(obs*mini))

Leemos el dataset

In [3]:
pd_fraud = pd.read_csv("../data/Base.csv",low_memory=False)
pd_fraud
Out[3]:
fraud_bool income name_email_similarity prev_address_months_count current_address_months_count customer_age days_since_request intended_balcon_amount payment_type zip_count_4w velocity_6h velocity_24h velocity_4w bank_branch_count_8w date_of_birth_distinct_emails_4w employment_status credit_risk_score email_is_free housing_status phone_home_valid phone_mobile_valid bank_months_count has_other_cards proposed_credit_limit foreign_request source session_length_in_minutes device_os keep_alive_session device_distinct_emails_8w device_fraud_count month
0 1 0.9 0.166828 -1 88 50 0.020925 -1.331345 AA 769 10650.765523 3134.319630 3863.647740 1 6 CA 185 0 BA 1 0 24 0 500.0 0 INTERNET 3.888115 windows 0 1 0 7
1 1 0.9 0.296286 -1 144 50 0.005418 -0.816224 AB 366 534.047319 2670.918292 3124.298166 718 3 CA 259 1 BA 0 0 15 0 1500.0 0 INTERNET 31.798819 windows 0 1 0 7
2 1 0.9 0.044985 -1 132 40 3.108549 -0.755728 AC 870 4048.534263 2893.621498 3159.590679 1 14 CB 177 1 BA 0 1 -1 0 200.0 0 INTERNET 4.728705 other 0 1 0 7
3 1 0.9 0.159511 -1 22 50 0.019079 -1.205124 AB 810 3457.064063 4054.908412 3022.261812 1921 6 CA 110 1 BA 0 1 31 1 200.0 0 INTERNET 2.047904 linux 0 1 0 7
4 1 0.9 0.596414 -1 218 50 0.004441 -0.773276 AB 890 5020.341679 2728.237159 3087.670952 1990 2 CA 295 1 BA 1 0 31 0 1500.0 0 INTERNET 3.775225 macintosh 1 1 0 7
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
999995 0 0.6 0.192631 -1 104 40 0.030592 -1.044454 AB 804 7905.711839 8341.468557 4972.635997 1 8 CA 75 1 BC 1 1 25 0 200.0 0 INTERNET 8.511502 linux 1 1 0 4
999996 0 0.8 0.322989 148 9 50 1.628119 -1.409803 AC 3306 5391.470463 4955.170808 5022.728108 0 2 CC 154 1 BC 1 1 -1 0 200.0 0 INTERNET 8.967865 windows 0 1 0 4
999997 0 0.8 0.879403 -1 30 20 0.018563 34.692760 AA 1522 8063.102636 5670.654316 4377.196321 2023 6 CF 64 0 BC 0 1 11 0 200.0 0 INTERNET 8.195531 other 0 1 0 4
999998 0 0.9 0.762112 -1 189 20 0.015352 94.661055 AA 1418 8092.641762 3982.582204 4394.803296 1678 6 CA 163 0 BA 1 0 28 0 500.0 0 INTERNET 4.336064 windows 1 1 0 4
999999 0 0.2 0.697452 -1 321 20 2.655916 9.908499 AA 951 6169.630036 3695.308261 4352.334543 2 12 CA 36 1 BE 0 1 15 0 200.0 0 INTERNET 6.717022 linux 0 1 0 4

1000000 rows × 32 columns

In [4]:
print(len(pd_fraud.columns))
32

Vemos los dintitos tipos de variables que tenemos y como su distribución.

In [5]:
pd_fraud.dtypes.sort_values().to_frame('feature_type').groupby(by = 'feature_type').size().to_frame('count').reset_index()
Out[5]:
feature_type count
0 int64 18
1 float64 9
2 object 5

Vemos rápidamente la distribución de la variable objetivo y vemos que está muy desbalanceada a favor de el 0, que indica no fraude.

In [6]:
pd_fraud.fraud_bool.value_counts()
Out[6]:
fraud_bool
0    988971
1     11029
Name: count, dtype: int64

Vemos las distintas columnas que componen en dataset

In [7]:
pd_fraud.columns
Out[7]:
Index(['fraud_bool', 'income', 'name_email_similarity',
       'prev_address_months_count', 'current_address_months_count',
       'customer_age', 'days_since_request', 'intended_balcon_amount',
       'payment_type', 'zip_count_4w', 'velocity_6h', 'velocity_24h',
       'velocity_4w', 'bank_branch_count_8w',
       'date_of_birth_distinct_emails_4w', 'employment_status',
       'credit_risk_score', 'email_is_free', 'housing_status',
       'phone_home_valid', 'phone_mobile_valid', 'bank_months_count',
       'has_other_cards', 'proposed_credit_limit', 'foreign_request', 'source',
       'session_length_in_minutes', 'device_os', 'keep_alive_session',
       'device_distinct_emails_8w', 'device_fraud_count', 'month'],
      dtype='object')

Al leer la información del Dataset vemos que hay columnas que son categóricas. Hacemos una lista con esas variables y las numéricas. Esto nos será útil para futuras operaciones.

In [8]:
lista_variables_categoricas = ["payment_type","employment_status","housing_status","email_is_free","phone_home_valid",
                                "phone_mobile_valid","has_other_cards","foreign_request","source","device_os","keep_alive_session"]
lista_variables_numericas = ["income", "name_email_similarity", "prev_address_months_count", "current_address_months_count", "customer_age", "days_since_request",
                            "intended_balcon_amount", "zip_count_4w", "velocity_6h", "velocity_24h", "velocity_4w", "bank_branch_count_8w", "date_of_birth_distinct_emails_4w",
                            "credit_risk_score", "bank_months_count", "proposed_credit_limit", "session_length_in_minutes", "device_distinct_emails_8w", "device_fraud_count",
                            "month"]
target="fraud_bool"

Vemos la cantidad de filas y columnas del test originalmente y después de borrar los duplicados.Como podemos observar es la misma cantidad, por lo que en nuestro Dataset no tenemos duplicados.

In [9]:
print(pd_fraud.shape, pd_fraud.drop_duplicates().shape)
(1000000, 32) (1000000, 32)

Observamos tambien si el dataset contiene columnas duplicadas utilizando la función duplicate_columns. El restultado es que el dataset no contiene columnas duplicadas.

In [10]:
duplicate_cols = duplicate_columns(pd_fraud)
In [11]:
duplicate_cols
Out[11]:
[]
In [12]:
pd_fraud.shape
Out[12]:
(1000000, 32)

Vemos los valores únicos de cada columna para ver si estamos de acuerdo con la información sobre el dataset en referencia al tipo de dato que es cada columna.

Al ver los valores únicos concordamos con la información del dataset en relación a las variables que son categóricas y que son numéricas.

In [13]:
for i in pd_fraud.columns:
    print(i, pd_fraud[i].unique())
fraud_bool [1 0]
income [0.9 0.3 0.7 0.6 0.4 0.2 0.8 0.1 0.5]
name_email_similarity [0.16682773 0.29628601 0.04498549 ... 0.87940313 0.76211215 0.69745243]
prev_address_months_count [ -1  92 172  28 289  35  55 194 103  27  34  58  61  12  57  53  96  30
 111  26  86  64  18 234  97  56  54 125 110  65 217  93 126  25  33  80
  99  45 104  87  49  62 282 101 135 154  36  14  91  83  10  32  51 325
 333 159  11 318 109 152 288 102 336 202 225 118 115 319 201  47  37 241
  60  13 165  66  94  95  98  59  29  89  16 304 211 314  22  31  63 321
 141 112  52 151 142   9  50 173  90 307 106  78  24 195 310   8 146 284
  85  44 160  23 100  38  48 327 140 105 122  67 127  21 119   7 156 179
  88 107  82  42 145 324  39 306 339 209 294  72  79 246 114  84 137  46
 188 220 139  74 312 239  75  20 161 168 290  68 187 303 116 230 297  77
 203 181 177  41 291 123 124 108 295 308 133 183 278  43 121 130 328 113
 189 300 222 199  70 196 216 355  15  81 315 117 131 298 157  76  40 213
 338 237 164 178 320 252 192 197 148 132 138 208 285 311 134 153 171 190
 166 193 120 309 316 305 231 253 155 280 235 287 347 205  71  19 273 232
 343 301 144 191 143 335 221 174 326 262 329 332 215 170 342 331 150 228
 218 149 182 323 286 313 163 185 206 147 341 247 351 243 344 223 128 251
 352 219 269 180 175 184 236 158 176  69 226 238 317 322 129 136 293 296
 210 302 245 162 345 244 279 264 227 167 353 330 186 334  73 265 275 169
 276 260 363 242 299 250 198 256 229 212 224 254 274  17 340 233 292   6
 346 267 207 371 350 240 358 200 272 361 277 214 283 357 266 248 348 204
 257 337 281 362 377 364 354 261 263 258 349 249 381 359 255 268 271 259
 270 356 374   5 360 366 365 372 367 369 375 383 370 373]
current_address_months_count [ 88 144 132  22 218  30 152  18  64  60 131 109 107 123  37  55 173  50
  94 153  82   9  19 184  56   5  90  78   3 162  34 112  23  99  44  51
 140  62  98 135 209  72 229 185  14  71 239 183  16 141 186  26  63 142
  80 199 119   8   2 118 272 306 216 212  48 151 189 117 124 203 130  29
 176 120  57 104 247 143 128 149   0 122 129 360 106 170 101  15  74 220
 363 236 159  69  93  27  12 356 146   6  36  32  83 273 155  45 349  41
  17 361 188  79  -1 171 111 138  68 169 175  47  39 160 113  13   7 270
  25 274 296 377  38  35  54  89 105  11 156 167  33 194 290 145  31 381
 215  43 136  61 178  42  85  84  58  49 102  76 103 177   1  73 126  70
 139 181 250 366 125 121 137 147  66 154 328  52 232 295 100  87 257  65
 320 180   4 164 249  20  67  40 114 193 278 150 195  81  77 127 248 225
 244 242 297  97  95 224 148  24 234 227 222 158 190  86  91 172  96  59
 198 182  46 217 174  75 165 108 133 204 134 219 321 206  53  10 163 316
 240 291 214 317 230 255 243 157 197  28 233 116 110 246 277  92 115 208
 281 301 251 282 372 357 191 289 269 373 205 376 207 298 354 369 370 285
 374 166 238 161 201 241 228 358 362 283 235 231 200 179 265 384 304 338
  21 226 254 293 310 262 340 192 347 319 309 353 202 331 302 288 280 367
 333 352 387 196 276 322 187 213 268 266 223 210 284 267 256 371 311 313
 275 299 359 271 279 253 308 245 334 211 312 221 258 382 260 259 346 261
 237 326 368 168 364 263 303 292 307 264 375 337 343 365 252 315 287 329
 355 342 314 325 300 380 345 324 294 344 335 383 327 336 323 318 351 379
 286 339 378 305 350 389 330 332 348 341 385 399 398 386 388 391 390 425
 407 392 393 395 410 394 397 396 401 412 404 402 400 417 424 406 408 418
 419 403 411 405 428 416 413 414 409]
customer_age [50 40 30 20 70 60 80 10 90]
days_since_request [0.02092517 0.00541754 3.10854879 ... 0.01856287 0.01535159 2.65591566]
intended_balcon_amount [-1.33134496 -0.81622375 -0.7557277  ... 34.69276003 94.66105465
  9.90849898]
payment_type ['AA' 'AB' 'AC' 'AD' 'AE']
zip_count_4w [ 769  366  870 ... 5906 6243 6189]
velocity_6h [10650.76552277   534.04731894  4048.53426315 ...  8063.10263573
  8092.64176182  6169.63003565]
velocity_24h [3134.31963049 2670.91829173 2893.62149796 ... 5670.65431579 3982.58220422
 3695.30826066]
velocity_4w [3863.64773953 3124.29816559 3159.59067885 ... 4377.19632143 4394.80329639
 4352.33454309]
bank_branch_count_8w [   1  718 1921 ... 2371 2339 2359]
date_of_birth_distinct_emails_4w [ 6  3 14  2 13 10  1  4  5  9 11  8  7 17 21 12  0 18 19 15 22 16 24 23
 20 25 27 29 32 30 26 28 33 31 35 34 37 38 36 39]
employment_status ['CA' 'CB' 'CC' 'CD' 'CE' 'CF' 'CG']
credit_risk_score [ 185  259  177  110  295  199  272   83  222  118  229   95  296  172
  119   12  302  181  234  115  308  201  125  299  275  319  220  106
  112  251  108  145   50  171  103  166  189   24  215  236  223  320
  214  269  105  113  193  143  278  155  205   49  111  317  161  148
  257   98  281  190  158  169  238  211  227  228  276  273  290  117
  194  129  123   77  130  216  121  282  359  241  191  197  219  206
  151  180  285  163  249  200  233  224  204  127  192  154  132   91
  237  164  114  202  209  186   81  182  245  264  339  221   64  304
  218  183  328   93   43  137  142  316  178   85  305  167  314   97
  140  247   92  149   53  338  128  175  235  107  104  198  176  102
  187  291  300  254  287  173  326   89  133   88  318   99  101  138
  170   62  289   21  265  203  134   70  136  303  195  345  271   69
  242  232  213  343  122   75  270  307   48   32  297  208    4  100
   45  184  147  324  159   60  165   80  226  210  298  329   82   16
   74  131  243  301  225   87  162  188  322  277  280  274  109  139
  126   90   66  250   76  240   61   79  152  283  341  231   78  153
  258   37  116  248   96   63  286  230  262  263   57  246   94   58
  309  146  279  179  160  156  323   86  252   71  344  306  356   55
  239  207   40  351   84   72  288  336  266  255  261  174  124  244
  212  168   14   38  293  294  312  196  120  267   15  157  292  141
  260  340  253  311  135   46  346  352   28   56  325  268   67  354
  217  310  331  150  333  256  315  327   51   65    8   68   25  364
  332  337   19  348  284   29  144   52  335  321   59   73   42   39
   11   44   33  -69  347    9  -21   35  -26   54   22  313  365   30
  -42   47  334   -2  342    0  -38  -34    7   26   31  -55  353   20
    2  -43   34  -16   23    5   18  -58   27  -28   36  -46   41  349
   13  330  -20  -29  -86   -5    6  -73  -81  355   -1  -54  -45  -10
  -63  -70  -83   -3   10  -35   -8  -85   -9  -91  -78  -62  -15   -7
  357  375  -32   17  373  361 -103  -40  -75  -31  -33  -52  -51  -77
  -48  -27  -44  -66 -114  -13  -11  -47  -61   -4   -6  -17  -30  -67
  -24  -57  -39  -88  -36    1 -125  -93  -41  -14  -79  -12 -111  -64
  -23  -92  -37    3  -25  369  -56  358  -18  362  -84  -50  -99  -65
  360  371  -53  -89  350  -19  -80  -71  -68  -74 -107  -60  -87  374
 -123  383  -72  -49  366  -22  -59  -82 -126 -115  -76 -147 -104  -96
 -118 -121  -90  -95  363  -97 -100 -144 -136  367  378 -109  -94 -170
 -116  380  368 -102  377 -129 -139  -98 -106  370 -117 -131 -105 -101
 -127 -134 -110 -124 -113 -122 -153 -108 -149 -141 -135 -119 -165 -112
 -164 -154 -137 -130 -128 -143 -140 -148 -132 -157 -133 -146 -145 -158
 -120 -150 -168  376 -152 -138  385 -156 -166 -159 -169 -155  372  387
  386  389  379  381 -167]
email_is_free [0 1]
housing_status ['BA' 'BB' 'BC' 'BD' 'BE' 'BF' 'BG']
phone_home_valid [1 0]
phone_mobile_valid [0 1]
bank_months_count [24 15 -1 31 30 28 25  1 11 22  9 21  5 10  2 19 27  4  3 20 26 29  6 14
 16  7 12 18 23 32 13  8 17]
has_other_cards [0 1]
proposed_credit_limit [ 500. 1500.  200. 2000. 1900. 1000.  990.  490.  510.  190.  210. 2100.]
foreign_request [0 1]
source ['INTERNET' 'TELEAPP']
session_length_in_minutes [ 3.8881146  31.79881936  4.72870487 ...  8.19553116  4.33606391
  6.71702157]
device_os ['windows' 'other' 'linux' 'macintosh' 'x11']
keep_alive_session [0 1]
device_distinct_emails_8w [ 1  2  0 -1]
device_fraud_count [0]
month [7 3 2 1 6 0 5 4]

Convertimos los tipos de columnas en diccionario. Es una forma de ver que tipo de dato es cada columna y si concuerda con la información que se nos proporciona en el dataset.

In [14]:
pd_fraud.dtypes.to_dict()
Out[14]:
{'fraud_bool': dtype('int64'),
 'income': dtype('float64'),
 'name_email_similarity': dtype('float64'),
 'prev_address_months_count': dtype('int64'),
 'current_address_months_count': dtype('int64'),
 'customer_age': dtype('int64'),
 'days_since_request': dtype('float64'),
 'intended_balcon_amount': dtype('float64'),
 'payment_type': dtype('O'),
 'zip_count_4w': dtype('int64'),
 'velocity_6h': dtype('float64'),
 'velocity_24h': dtype('float64'),
 'velocity_4w': dtype('float64'),
 'bank_branch_count_8w': dtype('int64'),
 'date_of_birth_distinct_emails_4w': dtype('int64'),
 'employment_status': dtype('O'),
 'credit_risk_score': dtype('int64'),
 'email_is_free': dtype('int64'),
 'housing_status': dtype('O'),
 'phone_home_valid': dtype('int64'),
 'phone_mobile_valid': dtype('int64'),
 'bank_months_count': dtype('int64'),
 'has_other_cards': dtype('int64'),
 'proposed_credit_limit': dtype('float64'),
 'foreign_request': dtype('int64'),
 'source': dtype('O'),
 'session_length_in_minutes': dtype('float64'),
 'device_os': dtype('O'),
 'keep_alive_session': dtype('int64'),
 'device_distinct_emails_8w': dtype('int64'),
 'device_fraud_count': dtype('int64'),
 'month': dtype('int64')}

En el diccionario anterior se observa que Python interpreto las columnas que eran categoricas como otro tipo de dato. Por ello realizamos un bucle que nos convierta esas columnas en datos categóricos.

In [15]:
for columna in lista_variables_categoricas:
    pd_fraud[columna] = pd_fraud[columna].astype('category')

Convertimos nuestra columna objetivo, que es la que nos indica si hay o no hay fraude en string.

In [16]:
pd_fraud["fraud_bool"] = pd_fraud["fraud_bool"].astype(str)

Realizamos una lista con aquellas columnas que su tipo de dato es float. Además, aunque pueda sonar redundante, dentro de nuestro actual dataframe convertimos todas las columnas que están en esa lista a float.Eso nos da la certeza de que los datos están realmente como necesitamos. Estas conversiones serán importantes posteriormente a la hora de elaborar los gráficos.

In [17]:
list_var_continuous = list(pd_fraud.select_dtypes('float').columns)
pd_fraud[list_var_continuous] = pd_fraud[list_var_continuous].astype(float)
pd_fraud.dtypes
Out[17]:
fraud_bool                            object
income                               float64
name_email_similarity                float64
prev_address_months_count              int64
current_address_months_count           int64
customer_age                           int64
days_since_request                   float64
intended_balcon_amount               float64
payment_type                        category
zip_count_4w                           int64
velocity_6h                          float64
velocity_24h                         float64
velocity_4w                          float64
bank_branch_count_8w                   int64
date_of_birth_distinct_emails_4w       int64
employment_status                   category
credit_risk_score                      int64
email_is_free                       category
housing_status                      category
phone_home_valid                    category
phone_mobile_valid                  category
bank_months_count                      int64
has_other_cards                     category
proposed_credit_limit                float64
foreign_request                     category
source                              category
session_length_in_minutes            float64
device_os                           category
keep_alive_session                  category
device_distinct_emails_8w              int64
device_fraud_count                     int64
month                                  int64
dtype: object

Observamos que los cambios se han realizado correctamente.

In [18]:
pd_fraud.dtypes
Out[18]:
fraud_bool                            object
income                               float64
name_email_similarity                float64
prev_address_months_count              int64
current_address_months_count           int64
customer_age                           int64
days_since_request                   float64
intended_balcon_amount               float64
payment_type                        category
zip_count_4w                           int64
velocity_6h                          float64
velocity_24h                         float64
velocity_4w                          float64
bank_branch_count_8w                   int64
date_of_birth_distinct_emails_4w       int64
employment_status                   category
credit_risk_score                      int64
email_is_free                       category
housing_status                      category
phone_home_valid                    category
phone_mobile_valid                  category
bank_months_count                      int64
has_other_cards                     category
proposed_credit_limit                float64
foreign_request                     category
source                              category
session_length_in_minutes            float64
device_os                           category
keep_alive_session                  category
device_distinct_emails_8w              int64
device_fraud_count                     int64
month                                  int64
dtype: object
In [19]:
pd_fraud.dtypes.to_dict()
Out[19]:
{'fraud_bool': dtype('O'),
 'income': dtype('float64'),
 'name_email_similarity': dtype('float64'),
 'prev_address_months_count': dtype('int64'),
 'current_address_months_count': dtype('int64'),
 'customer_age': dtype('int64'),
 'days_since_request': dtype('float64'),
 'intended_balcon_amount': dtype('float64'),
 'payment_type': CategoricalDtype(categories=['AA', 'AB', 'AC', 'AD', 'AE'], ordered=False, categories_dtype=object),
 'zip_count_4w': dtype('int64'),
 'velocity_6h': dtype('float64'),
 'velocity_24h': dtype('float64'),
 'velocity_4w': dtype('float64'),
 'bank_branch_count_8w': dtype('int64'),
 'date_of_birth_distinct_emails_4w': dtype('int64'),
 'employment_status': CategoricalDtype(categories=['CA', 'CB', 'CC', 'CD', 'CE', 'CF', 'CG'], ordered=False, categories_dtype=object),
 'credit_risk_score': dtype('int64'),
 'email_is_free': CategoricalDtype(categories=[0, 1], ordered=False, categories_dtype=int64),
 'housing_status': CategoricalDtype(categories=['BA', 'BB', 'BC', 'BD', 'BE', 'BF', 'BG'], ordered=False, categories_dtype=object),
 'phone_home_valid': CategoricalDtype(categories=[0, 1], ordered=False, categories_dtype=int64),
 'phone_mobile_valid': CategoricalDtype(categories=[0, 1], ordered=False, categories_dtype=int64),
 'bank_months_count': dtype('int64'),
 'has_other_cards': CategoricalDtype(categories=[0, 1], ordered=False, categories_dtype=int64),
 'proposed_credit_limit': dtype('float64'),
 'foreign_request': CategoricalDtype(categories=[0, 1], ordered=False, categories_dtype=int64),
 'source': CategoricalDtype(categories=['INTERNET', 'TELEAPP'], ordered=False, categories_dtype=object),
 'session_length_in_minutes': dtype('float64'),
 'device_os': CategoricalDtype(categories=['linux', 'macintosh', 'other', 'windows', 'x11'], ordered=False, categories_dtype=object),
 'keep_alive_session': CategoricalDtype(categories=[0, 1], ordered=False, categories_dtype=int64),
 'device_distinct_emails_8w': dtype('int64'),
 'device_fraud_count': dtype('int64'),
 'month': dtype('int64')}

Hacemos un conteo de los datos de fraud_bool para realizar posteriormente un gráfico. Así sabremos la proporción de Fraude y No fraude que tenemos. El 0 representa No fraude mientras que el 1 el Fraude.

Primero lo hacemos con porcentaje y después en valores absolutos.

In [20]:
pd_plot_fraud_bool = pd_fraud['fraud_bool']\
        .value_counts(normalize=True)\
        .mul(100).rename('percent').reset_index()
In [21]:
pd_plot_fraud_bool
Out[21]:
fraud_bool percent
0 0 98.8971
1 1 1.1029
In [22]:
pd_plot_fraud_bool_conteo = pd_fraud['fraud_bool'].value_counts().reset_index()
pd_plot_fraud_bool_conteo
Out[22]:
fraud_bool count
0 0 988971
1 1 11029
In [23]:
pd_plot_fraud_bool_pc = pd_plot_fraud_bool.merge(pd_plot_fraud_bool_conteo, on='fraud_bool', how='inner')
pd_plot_fraud_bool_pc
Out[23]:
fraud_bool percent count
0 0 98.8971 988971
1 1 1.1029 11029

Realizamos el gráfico y se puede observar tanto númericamente como graficamente que el porcentaje de Fraude es muy reducido en comparacion al de No fraude.

In [24]:
fig=px.bar(pd_plot_fraud_bool_pc,x='fraud_bool',y='percent')
In [25]:
fig.show()

Ahora vamos a analizar los valores nulos

En la documentacion podemos observar que existían variables con missing. Por ello realizamos una lista que contenga todas esas columnas para posteriormente tratarlas.

Todas esas columnas tienen como nulos los valores iguales a -1 menos 'intended_balcon_amount' que todos los valores negativos que tengan son missings. Hacemos una lista con esas columnas.

In [26]:
variables_con_missings=['prev_address_months_count','current_address_months_count','intended_balcon_amount', 'bank_months_count', 'session_length_in_minutes', 'device_distinct_emails_8w']
In [27]:
variables_con_missings
Out[27]:
['prev_address_months_count',
 'current_address_months_count',
 'intended_balcon_amount',
 'bank_months_count',
 'session_length_in_minutes',
 'device_distinct_emails_8w']

Realizamos dos listas con el objetivo de rellenarlas a través del bucle que se explicará a continuación. Con estas dos listas posteriormente haremos un dataframe.

In [28]:
missing_count = []
missing_percentage = []

Este bucle lo que realiza es contar el número de nulos que tiene cada columna en función del tipo de dato que sea nulo para cada una. Además luego realiza un porcentaje de nulos al comparar la cantidad resultante con la cantidad total.

In [29]:
for variable in variables_con_missings:
    if variable == 'intended_balcon_amount':
        missing_count.append(pd_fraud[pd_fraud[variable] < 0].shape[0])
    else:
        missing_count.append(pd_fraud[pd_fraud[variable] == -1].shape[0])

    missing_percentage.append((missing_count[-1] / len(pd_fraud)) * 100)
In [30]:
missing_count
Out[30]:
[712920, 4254, 742523, 253635, 2015, 359]
In [31]:
missing_percentage
Out[31]:
[71.292, 0.4254, 74.2523, 25.3635, 0.20149999999999998, 0.0359]

Realizamos un data frame con los resultados obtenidos.

In [32]:
df_missing_data = pd.DataFrame({
    'Variable': variables_con_missings,
    'Missing': missing_count,
    'Porcentaje Missing': missing_percentage
})

Ahora mismo estos nulos nos sirven para situarnos y saber que cantidad tenemos. Posteriormente a la hora de realizar el modelo veremos como los tratamos.

In [33]:
df_missing_data
Out[33]:
Variable Missing Porcentaje Missing
0 prev_address_months_count 712920 71.2920
1 current_address_months_count 4254 0.4254
2 intended_balcon_amount 742523 74.2523
3 bank_months_count 253635 25.3635
4 session_length_in_minutes 2015 0.2015
5 device_distinct_emails_8w 359 0.0359

Ahora haremos lo mismo pero pasa saber los missing por fila. Contaremos el numero de nulos en cada fila y luego lo dividiremos entre la el numero de columnas que tiene el dataframe.

De esta forma sabremos el porcentaje de missing que tenemos en cada fila.

In [34]:
missing_counts = pd_fraud.apply(calcular_missing_por_fila, axis=1)
missing_percentages = (missing_counts / len(pd_fraud.columns)) * 100

Elaboramos un dataframe para poder visualizarlo correctamente.

In [35]:
missing_data = pd.DataFrame({
    'Row_Index': missing_counts.index,
    'Missing_Count': missing_counts.values,
    'Missing_Percentage': missing_percentages.values
})
In [36]:
missing_data
Out[36]:
Row_Index Missing_Count Missing_Percentage
0 0 2 6.250
1 1 2 6.250
2 2 3 9.375
3 3 2 6.250
4 4 2 6.250
... ... ... ...
999995 999995 2 6.250
999996 999996 2 6.250
999997 999997 1 3.125
999998 999998 1 3.125
999999 999999 1 3.125

1000000 rows × 3 columns

Como no podemos visualizar todo el dataset vamos a buscar el valor máximo de datos faltantes por fila para ver si hay alguna fila con mas de un 70 % de datos faltantes para proceder a eliminar debido a que no aportaría información. Como podemos observar el máximo es 15.6 % por lo que ninguna fila será eliminada por datos faltantes.

In [37]:
max_missing_percentage = missing_data['Missing_Percentage'].max()
max_missing_percentage
Out[37]:
15.625

Elaboramos una copia del dataframe original para realizar gráficos ya que queremos hemos elaborados una función que además de hacer diferentes gráficos también nos cuenta el numero de nulos. Para ello deben estar los nulos como Nan.

In [38]:
pd_fraud_graficos = pd_fraud.copy()
In [39]:
for column in variables_con_missings:
    if column == 'intended_balcon_amount':
        pd_fraud_graficos.loc[pd_fraud_graficos[column] < 0, column] = np.nan
    else:
        pd_fraud_graficos[column].replace(-1, np.nan, inplace=True)

Comprobamos que ha hecho bien la conversión.

In [40]:
pd_fraud_graficos['prev_address_months_count'].isnull().sum()
Out[40]:
712920

Calculamos el porcentaje de valores faltantes para cada variables en variables_con_missings, agrupados por la condición de fraude indicada en la columna fraud_bool. De esta forma podemos observar si ciertas características tienen más valores faltantes en transacciones fraudulentas comparadas con transacciones legítimas, o viceversa. Esto podría indicar que la falta de información en ciertas variables está de alguna manera asociada con actividades fraudulentas.

La alta tasa de valores faltantes para prev_address_months_count en transacciones fraudulentas (91.89%) en comparación con las transacciones no fraudulentas (71.06%) sugiere que la omisión de la información de la dirección anterior es más común en las transacciones fraudulentas. Esto puede ser un indicador de comportamiento sospechoso y podría ser un factor importante para detectar fraude.

El porcentaje de 88.38% de valores faltantes en transacciones fraudulentas indica que casi siempre falta la cantidad transferida inicialmente en estos casos, lo que es significativamente más alto que en las transacciones no fraudulentas (74.09%). Esto podría interpretarse como que en las transacciones fraudulentas, a menudo no se reporta o se reporta erróneamente la cantidad de la transacción, lo que puede ser una técnica utilizada para ocultar el fraude.

Por lo tanto, si teniamos la duda de eliminar prev_adress_months_count y intended_balcon_amount por su elevada cantidad de nulos llegamos a la conclusión de que no debemos eliminarla ya que eliminar esta variable podría hacer se pierda información valiosa que puede ser indicativa de comportamiento fraudulento.

In [41]:
missing_vals = pd.DataFrame()

for variable in variables_con_missings:
    missing_vals_col = pd_fraud_graficos.groupby('fraud_bool')[variable].apply(lambda x: round(x.isna().sum()/len(x) * 100, 2))
    missing_vals[variable] = missing_vals_col

print(missing_vals)
            prev_address_months_count  current_address_months_count  \
fraud_bool                                                            
0                               71.06                          0.43   
1                               91.89                          0.13   

            intended_balcon_amount  bank_months_count  \
fraud_bool                                              
0                            74.09              25.23   
1                            88.38              37.54   

            session_length_in_minutes  device_distinct_emails_8w  
fraud_bool                                                        
0                                0.20                       0.04  
1                                0.16                       0.04  

Realizamos un bucle utiliza la función plot_feature para generar visualizaciones de las diferentes variables del conjunto de datos , con un enfoque particular en su relación con la variable objetivo 'fraud_bool'.

La función plot_feature visualiza datos de un DataFrame en dos tipos de gráficos diferentes, dependiendo de si la variable es continua o categórica. Si la variable es continua, muestra un histograma y un diagrama de caja para ver la distribución y compararla con una variable objetivo. Si la variable es categórica, muestra un gráfico de conteo y un gráfico de barras de proporciones para ver cuántas veces aparece cada categoría y cómo se relaciona con la variable objetivo. También informa la cantidad de valores nulos.

Aplicaremos la función tanto en variables numéricas como categóricas, proporcionando una visión detallada de cómo cada variable se relaciona con la variable objetivo 'fraud_bool'.

In [42]:
for variable in lista_variables_numericas:
    plot_feature(df=pd_fraud_graficos, col_name=variable,isContinuous=True,target='fraud_bool')
    
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
In [43]:
warnings.filterwarnings('ignore')
for variable in lista_variables_categoricas:
    plot_feature(df=pd_fraud_graficos, col_name=variable,isContinuous=False,target='fraud_bool')
    
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image

Vamos a comentar ahora los gráficos que nos aportan información mas relevante:

En el BoxPlot "income by fraud bool" podemos ver que las cuentas fraudulentas tienen una menor dispersión y que además la mediana en income es superior. Esto parece indicar que las cuentas que realizan fraude son aquellas con más income.

En el gráfico en el que se compara la variable "fraud_bool" con la variable "customer age" podemos observar que a medida que el rango de edad aumenta hay un fraude mayor, por lo que debería ser un factor a tener en cuenta.

Comparando el "name_email_similarity" se puede observar que cuando hacen fraude la similitud del email y del nombre del aplicante es menor que cuando no lo hacen.

En el gráfico "proposed_credit_limit by fraud bool" sugiere que en los casos de fraude los límites de crédito propuestos son más altos y con una mayor variabilidad en estos límites en comparación con los casos no fraudulentos. Sin embargo, hay ciertos casos no fraudulentos que tienen límites de crédito propuestos extremadamente altos, como lo indican los valores atípicos.

En el gráfico que compara "fraud bool" con "has_another card" lo que podemos observar es que en la mayoría de casos de fraude los individuos no poseían otra tarjeta de crédito, solamente una pequeña proporción de ellos poseían otra tarjeta.

Comparando "fraud_bool" con "device_os" se observa que Windows es el sistema operativo en el que se realiza más fraude.

El tiempo que pasan los usuarios en la página web del banco no es representativo de si se va a efectuar fraude o no.

En el boxplot que relaciona "intended_balcon_amount" con "fraud_bool" podemos observar que en los casos de fraude la cantidad que suelen ingresar al principio los aplicantes es menor que cuando no hay fraude. Además podemos observar muchos valores atípicos en ambos grupos, lo que indica que tanto para transacciones fraudulentas como para transacciones no fraudulentas pueden llegar a tener cantidades extremadamente altas o bajas.

Además al analizar source vemos que vemos que a la hora de hacer fraude está muy repartido si se hace a través de internet o teleapp, siendo gráficamente indistinguible.

Un gráfico que nos aporta mucha información es en el que se analiza la variable "housing_status" ya que podemos ver que la mayoría de personas que realizan fraude es cuando pertenecen a la categoria BA.

En "payment_type" se debe prestar especial atención a aquellos pagos que son hechos de forma AB, AC y AD.

Por último aquellas personas con un "employment_status" que sea igual a CC y CG realizan un mayor fraude según el gráfico realizado.

Con esta funcion elaboramos un dataframe que nos diga el número de outliers que hay en cada variable presente en la lista "list_var_continuous" y el porcentaje de outliers en relacion a la cantidad total de datos en cada columna.

In [44]:
get_deviation_of_mean_perc(pd_fraud, list_var_continuous, "fraud_bool", 3)
Out[44]:
0 1 variable sum_outlier_values porcentaje_sum_null_values
0 0.988242 0.011758 days_since_request 17775 0.017775
1 0.990295 0.009705 intended_balcon_amount 18960 0.018960
2 0.99355 0.00645 velocity_6h 4341 0.004341
3 0.996289 0.003711 velocity_24h 539 0.000539
4 0.870349 0.129651 proposed_credit_limit 6155 0.006155
5 0.979952 0.020048 session_length_in_minutes 23593 0.023593

Podemos observar que no son cantidades representativas,el mayor porcentaje es 2,3% . Sin embargo en función del tipo de modelo que escojamos decidiremos que hacer con estos atípicos debido a que algunos modelos son más sensibles a los datos atípicos que otros. Además los outliers pueden contener información muy valiosa en ocasiones.

Por ejemplo, en nuestro caso, en la detección de fraude , los outliers pueden ser precisamente los casos que se quieren identificar. Por ello, a la hora de realizar el modelo decidiremos que hacer con estos datos.

Aquí lo que queremos comprobar es las correlaciones que existen entre las distintas varibles numéricas. Utilizamos la funcion get_corr_matrix que generará una matriz de correlación a partir del conjunto de datos proporcionado.

In [45]:
get_corr_matrix(dataset = pd_fraud[lista_variables_numericas], 
                metodo='pearson', size_figure=[10,8])
No description has been provided for this image
Out[45]:
0

Tal y como podemos observar las correlaciones entre las variables continuas no son del todo fuertes. La máxima correlacion que hay se encuentra en torno a 0,8 en valor absoluto. Sim embargo las siguientes que están en torno a 0,6 y 0,5 respectivamente, lo que nos indica, en el contexto práctico, que la correlacion es significativa y hay una asociación notable entre las variables, pero no lo suficientemente fuerte como para indicar que un cambio en una variable predecirá con alta precisión un cambio en la otra.

Destacar que la diagonal es 0 y no 1 porque se ha especificado así en la función para posteriormente al filtrar evitar ver los 1 que son de la variable consigo misma.

In [46]:
corr = pd_fraud[lista_variables_numericas].corr('pearson')
new_corr = corr.abs()
new_corr.loc[:,:] = np.tril(new_corr, k=-1) 
new_corr = new_corr.stack().to_frame('correlation').reset_index().sort_values(by='correlation', ascending=False)
new_corr[new_corr['correlation']>0.5]
Out[46]:
level_0 level_1 correlation
372 month velocity_4w 0.848100
313 proposed_credit_limit credit_risk_score 0.606141
371 month velocity_24h 0.549919
209 velocity_4w velocity_24h 0.539115

Aquí indentificamos y ordenamos todas las correlaciones fuertes (mayores de 0.5 en valor absoluto) entre las variables continuas del conjunto de datos de entrenamiento, lo cual es útil para comprobar que lo que interpretamos en el gráfico de arriba visualmente es correcto y entender poder fijarnos en las dependencias más fuertes que haya entre las variables.

Ahora vamos a utilizar cramer-v para poder ver la asociacion que existe entre las variables categóricas y la variable objetivo. Aquí 0 indica ninguna asociación mientras que 1 es una asociación perfecta.

Interpretamos lo siguiente:

Payment type: Podemos observar que cuando hay más fraude es con el tipo de pago AC y que segun el índice de Cramér sugiere que hay una asociacion muy baja entre el tipo de pago y si la asocación es fraudulenta o no.

Employment_status: Cuando el employment_status es CA hay más fraude , sin embargo la asociación entre las variables sigue siendo debil.

Housing_status: Podemos observar que cuando el housing status es BG no existe fraude. Asociación débil.

Email_is_free: el tipo de email (gratuito o no) no es un indicador fuerte o decisivo de fraude en las transacciones.

Phone_home_valid y phone_mobile_valid: Se puede observar que la mayoria de fraude tiene lugar cuando no se da el número de teléfono de casa pero si el teléfono móvil.Sin embargo el índice cramer es muy bajo.

Has_other_cards:Se puede observar que cuando hay fraude la mayoria de usuarios que lo realizan no tienen otra tarjeta de crédito. Sin embargo según el índice de cramer se puede ver que conocer el valor de una variable no proporciona mucha información sobre el valor de la otra.

Device_os: No hay practicamente relacion entre las variables. Eso si, la mayoria de fraude viene de usuarios de Windows.

Source: La mayoria de fraude tiene lugar a traves de INTERNET, aunque tambien es verdad que la gran mayoria de usuarios proceden de Internet.

In [47]:
for var in lista_variables_categoricas:
    confusion_matrix = pd.crosstab(pd_fraud["fraud_bool"], pd_fraud[var])
    print(f"Matriz de confusión para {var}:")
    print(confusion_matrix)
    print(f"Valor de Cramér's V para {var}: {cramers_v(confusion_matrix.values)}")
    print("\n") 
Matriz de confusión para payment_type:
payment_type      AA      AB      AC      AD   AE
fraud_bool                                       
0             256885  366385  247862  117551  288
1               1364    4169    4209    1286    1
Valor de Cramér's V para payment_type: 0.039042842242482736


Matriz de confusión para employment_status:
employment_status      CA      CB     CC     CD     CE     CF   CG
fraud_bool                                                        
0                  721353  137335  36826  26422  22640  43949  446
1                    8899     953    932    100     53     85    7
Valor de Cramér's V para employment_status: 0.039579041948826


Matriz de confusión para housing_status:
housing_status      BA      BB      BC     BD      BE    BF   BG
fraud_bool                                                      
0               163318  259397  369855  25935  168553  1662  251
1                 6357    1568    2288    226     582     7    1
Valor de Cramér's V para housing_status: 0.11487732983320799


Matriz de confusión para email_is_free:
email_is_free       0       1
fraud_bool                   
0              466376  522595
1                3738    7291
Valor de Cramér's V para email_is_free: 0.027730004919501703


Matriz de confusión para phone_home_valid:
phone_home_valid       0       1
fraud_bool                      
0                 574685  414286
1                   8238    2791
Valor de Cramér's V para phone_home_valid: 0.03510388236282846


Matriz de confusión para phone_mobile_valid:
phone_mobile_valid       0       1
fraud_bool                        
0                   108676  880295
1                     1648    9381
Valor de Cramér's V para phone_mobile_valid: 0.013126368094879885


Matriz de confusión para has_other_cards:
has_other_cards       0       1
fraud_bool                     
0                766914  222057
1                 10098     931
Valor de Cramér's V para has_other_cards: 0.03513065449791526


Matriz de confusión para foreign_request:
foreign_request       0      1
fraud_bool                    
0                964284  24687
1                 10474    555
Valor de Cramér's V para foreign_request: 0.016824420425245186


Matriz de confusión para source:
source      INTERNET  TELEAPP
fraud_bool                   
0             982035     6936
1              10917      112
Valor de Cramér's V para source: 0.00373333855293764


Matriz de confusión para device_os:
device_os    linux  macintosh   other  windows   x11
fraud_bool                                          
0           330997      53074  340754   256999  7147
1             1715        752    1974     6507    81
Valor de Cramér's V para device_os: 0.08046708891771352


Matriz de confusión para keep_alive_session:
keep_alive_session       0       1
fraud_bool                        
0                   415792  573179
1                     7261    3768
Valor de Cramér's V para keep_alive_session: 0.05027680351314226


Realizamos lo mismo con un mapa de calor para ver ahora la asociación de las variables categóricas entre ellas y comprobamos que la asociación entre todas ellas es muy débil.

In [48]:
rows= []

for i1 in lista_variables_categoricas:
    col = []
    for i2 in lista_variables_categoricas:
        cramers = cramers_V(pd_fraud[i1], pd_fraud[i2]) 
        col.append(round(cramers,2))
    rows.append(col)

cramers_results = np.array(rows)
v_c = pd.DataFrame(cramers_results, columns =lista_variables_categoricas , index = lista_variables_categoricas)
plt.figure(figsize=(8,6))
sns.heatmap(v_c, cmap="YlGnBu", annot=True, linewidth=.5)
plt.show()
No description has been provided for this image

Por último,cambiamos la variable objetivo a entero debido a que muchos modelos de machine-learning requiere que la variable objetivo sea numérica.

In [51]:
pd_fraud["fraud_bool"] = pd_fraud["fraud_bool"].astype(int)
In [52]:
pd_fraud.to_pickle('../data/pd_fraud.pkl')